home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
CU Amiga Super CD-ROM 17
/
CU Amiga Magazine's Super CD-ROM 17 (1997)(EMAP Images)(GB)[!][issue 1997-12].iso
/
CUCD
/
Programming
/
DiceSource
/
src
/
vopts
/
reader.c
< prev
next >
Wrap
C/C++ Source or Header
|
1997-09-09
|
17KB
|
499 lines
/*
* (c)Copyright 1992-1997 Obvious Implementations Corp. Redistribution and
* use is allowed under the terms of the DICE-LICENSE FILE,
* DICE-LICENSE.TXT.
*/
#include "vopts.h"
Prototype int get_simple_token(char *buf);
Prototype int get_token(char *buf1, char *buf2, char *buf3);
Prototype struct G_OBJECT **newobj(struct G_OBJECT **objlist, int class, char *buf);
Prototype int parse_config(char * cfname);
Prototype int init_config(char *cfname);
Prototype void close_config(void);
/*********************************************************************
* TOKENS:
* TOK_EOF End of file
* '/''*' '*''/'
* TOK_STRING "string"
* TOK_STRING 'string'
* TOK_BAR BAR
* TOK_TEXT TEXT <string>
* TOK_BUTTON BUTTON <string>
* TOK_CHECK CHECK <string> <string> <string>
* TOK_CYCLE CYCLE <string>
* TOK_GROUP GROUP <string>
* TOK_LGROUP LGROUP <string>
* TOK_ITEM ITEM <string> <string>
* TOK_LIST LIST <string> <string> <string>
* TOK_MENU MENU <string>
* TOK_STRING STRING <string> <string>
* TOK_TITLE TITLE <string>
* TOK_VALUE VALUE <string> <string>
*/
#define TOK_ERROR -1
#define TOK_EOF 0 /* Note ordering of ERROR/EOF is assumed for easy tests below */
#define TOK_STRTOK 1
#define TOK_TOKEN 2
#define TOK_BAR 3
#define TOK_BUTTON 4
#define TOK_CHECK 5
#define TOK_CYCLE 6
#define TOK_GROUP 7
#define TOK_LGROUP 8
#define TOK_ITEM 9
#define TOK_LIST 10
#define TOK_MENU 11
#define TOK_STRING 12
#define TOK_TITLE 13
#define TOK_VALUE 14
#define TOK_TEXT 15
struct TOK_LOOK {
char toktype; /* Value to return for this token */
char token[6]; /* Note that we only allow 6 characters in a token */
char strings; /* Number of strings that the token takes as an argument */
};
struct TOK_LOOK tokens[] = {
{ TOK_BAR, "BAR ", 0},
{ TOK_BUTTON, "BUTTON", 1},
{ TOK_CHECK, "CHECK ", 3},
{ TOK_CYCLE, "CYCLE ", 1},
{ TOK_GROUP, "GROUP ", 1},
{ TOK_LGROUP, "LGROUP", 1},
{ TOK_ITEM, "ITEM ", 3},
{ TOK_LIST, "LIST ", 2},
{ TOK_MENU, "MENU ", 1},
{ TOK_STRING, "STRING", 2},
{ TOK_TITLE, "TITLE ", 1},
{ TOK_VALUE, "VALUE ", 2},
{ TOK_TEXT, "TEXT ", 1},
};
#define MAX_LOOK (sizeof(tokens)/sizeof(struct TOK_LOOK))
/*********************************************************************
* Character classes:
* 0 CL_EOF - EOF
* 1 CL_SLASH - /
* 2 CL_STAR - *
* 3 CL_DQUOTE - "
* 4 CL_SQUOTE - '
* 5 CL_ALPHA - A-Z a-z
* 6 CL_BLANK - ' ' \t \n
* 7 CL_OTHER - Anything else
*/
#define CL_EOF 0
#define CL_SLASH 1
#define CL_STAR 2
#define CL_DQUOTE 3
#define CL_SQUOTE 4
#define CL_ALPHA 5
#define CL_BLANK 6
#define CL_OTHER 7
#define MAX_CL 8
/*********************************************************************
* States:
* 0 ST_SCN - SCAN - Scanning - Looking for any character - skipping white space
* 1 ST_GSL - GOTSL - Found / - Checking for a matching * to start a comment
* 2 ST_CMT - CMT - Comment - Looking for a * to close a comment
* 3 ST_GST - GOTST - Found * - Checking for a / to close a comment
* 4 ST_DQT - DQUOTE - String " - Looking for a matching "
* 5 ST_SQT - SQUOTE - String ' - Looking for a matching '
* 6 ST_TOK - TOKEN - Token - Gathering a keyword token
*/
#define ST_SCN 0
#define ST_GSL 1
#define ST_CMT 2
#define ST_GST 3
#define ST_DQT 4
#define ST_SQT 5
#define ST_TOK 6
#define MAX_ST 7
#define MASK_ST 7
/*********************************************************************
* State Table Transitions:
*
* Character Class
* 0-EOF 1-/ 2-* 3-" 4-' 5-A-Z 6-Blank 7-Other
* State
* 0 SCAN END GOTSL ERROR DQUOTE SQUOTE >TOKEN SCAN ERROR
* 1 GOTSL ERROR ERROR CMT ERROR ERROR ERROR ERROR ERROR
* 2 CMT ERROR CMT GOTST CMT CMT CMT CMT CMT
* 3 GOTST ERROR SCAN CMT CMT CMT CMT CMT CMT
* 4 DQUOTE ERROR >DQUOTE >DQUOTE !SCAN >DQUOTE >DQUOTE >DQUOTE >DQUOTE
* 5 SQUOTE ERROR >SQUOTE >SQUOTE >SQUOTE !SCAN >SQUOTE >SQUOTE >SQUOTE
* 6 TOKEN +END +GOTSL ERROR +DQUOTE +SQUOTE >TOKEN +SCAN ERROR
* Note: > - AC_SAV - Means to append the current character and continue scan
* ! - AC_STR - Means to return the current token as a string.
* + - AC_TOK - Means to return the current token as a keyword
* ERROR - AC_ERR - Indicates issuing an error
* END - AC_END - Indicates returing the END token.
*/
#define AC_SKP (0<<5) /* Must be zero - the default to do nothing */
#define AC_SAV (1<<5)
#define AC_STR (2<<5)
#define AC_TOK (3<<5)
#define AC_ERR (4<<5)
#define AC_END (5<<5)
#define MASK_AC (7<<5)
char statetab[MAX_ST][MAX_CL] =
{
/* 0 ST_SCN */
{ AC_END, ST_GSL, AC_ERR, ST_DQT,
ST_SQT, AC_SAV|ST_TOK, ST_SCN, AC_ERR },
/* 1 ST_GSL */
{ AC_ERR, AC_ERR, ST_CMT, AC_ERR,
AC_ERR, AC_ERR, AC_ERR, AC_ERR },
/* 2 ST_CMT */
{ AC_ERR, ST_CMT, ST_GST, ST_CMT,
ST_CMT, ST_CMT, ST_CMT, ST_CMT },
/* 3 ST_GST */
{ AC_ERR, ST_SCN, ST_CMT, ST_CMT,
ST_CMT, ST_CMT, ST_CMT, ST_CMT },
/* 4 ST_DQT*/
{ AC_ERR, AC_SAV|ST_DQT, AC_SAV|ST_DQT, AC_STR|ST_SCN,
AC_SAV|ST_DQT, AC_SAV|ST_DQT, AC_SAV|ST_DQT, AC_SAV|ST_DQT },
/* 5 ST_SQT*/
{ AC_ERR, AC_SAV|ST_SQT, AC_SAV|ST_SQT, AC_SAV|ST_SQT,
AC_STR|ST_SCN, AC_SAV|ST_SQT, AC_SAV|ST_SQT, AC_SAV|ST_SQT },
/* 6 ST_TOK */
{ AC_TOK|ST_SCN, AC_TOK|ST_GSL, AC_ERR, AC_TOK|ST_DQT,
AC_TOK|ST_SQT, AC_SAV|ST_TOK, AC_TOK|ST_SCN, AC_ERR },
};
/*
* Get a simple token from the file.
* No parsing of semantics is done at this level.
*/
int get_simple_token(char *buf)
{
int c;
int pos;
int class;
int action;
pos = 0;
for(;;)
{
/* Get the next character from the input file and assign a character class */
c = getc(global.fp);
class = CL_OTHER;
switch(c)
{
case EOF: class = CL_EOF; break;
case '/': class = CL_SLASH; break;
case '*': class = CL_STAR; break;
case '"': class = CL_DQUOTE; break;
case '\'': class = CL_SQUOTE; break;
case '\n': global.line++;
case ' ':
case '\t': class = CL_BLANK; break;
default:
if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'))
class = CL_ALPHA;
break;
}
/* Run us through the state table to get an action and a new state */
action = statetab[global.state][class];
global.state = (action & MASK_ST);
/* Perform the work for the action */
switch(action & MASK_AC)
{
case AC_SAV: if (pos < 64) /* make sure we don't overflow the buffer */
{
buf[pos++] = c; /* Save the character */
break;
}
/* Else fall through to the error case */
case AC_ERR: return(TOK_ERROR);
case AC_STR: buf[pos] = 0; /* Null terminate what we gathered */
return(TOK_STRTOK);
case AC_TOK: buf[pos] = 0;
return(TOK_TOKEN);
case AC_END: return(TOK_EOF);
}
}
}
/*
* Get the next token and any associated strings.
*/
int get_token(char *buf1,
char *buf2,
char *buf3
)
{
int i;
int j;
int toktype;
int len;
char *strs[3];
strs[0] = buf1;
strs[1] = buf2;
strs[2] = buf3;
toktype = get_simple_token(buf1);
if (toktype == TOK_EOF) return(toktype);
if (toktype != TOK_TOKEN) return(TOK_ERROR);
/* Go through and uppercase the string */
/* we KNOW that it consists of only upper/lower case letters */
len = strlen(buf1);
if (len > 6) return(TOK_ERROR);
strcpy(buf1+len, " "); /* Make sure we blank pad for the memcmp */
for(i = 0; i < 6; i++)
if (buf1[i] >= 'a') buf1[i] -= ('a'-'A');
/* Now go through the table and look for a token */
for (i = 0; i < MAX_LOOK; i++)
if (!memcmp(buf1, tokens[i].token, 6)) break;
if (i == MAX_LOOK) return(TOK_ERROR);
/* We have a matching token, get any strings that it wants */
for(j = 0; j < tokens[i].strings; j++)
{
toktype = get_simple_token(strs[j]);
if (toktype != TOK_STRTOK) return(TOK_ERROR);
}
/* Everything checks out, let them know what type of token they got */
return((int)tokens[i].toktype);
}
void init_group(struct G_OBJECT *object)
{
while(object != NULL)
{
if (object->next) object->next->prev = object;
if (object->class == CLASS_CYCLE)
{
struct G_VALUE *val;
struct G_CYCLE *cyc;
cyc = (struct G_CYCLE *)object;
cyc->curval = cyc->values;
for(val = cyc->curval; val; val = val->next)
{
if (cyc->base.next) cyc->base.next->prev = (struct G_OBJECT *)cyc;
}
}
object = object->next;
}
}
/*
* Allocate an object structure to hold an entry
*/
static char sizetab[] = { 0,
sizeof(struct G_STRING),
sizeof(struct G_CYCLE),
sizeof(struct G_CHECK),
sizeof(struct G_GROUP),
sizeof(struct G_LIST)
};
struct G_OBJECT **newobj(struct G_OBJECT **objlist,
int class,
char *buf
)
{
struct G_OBJECT *obj;
obj = get_mem(sizetab[class]);
if (obj == NULL) return(NULL);
if (objlist) *objlist = obj;
obj->class = class;
obj->title = savestr(buf);
return(&obj->next);
}
/*
* Parse the configuration file
*/
int parse_config(char *cfname)
{
static char buf1[66], buf2[66], buf3[66];
int toktype;
int menupos;
int buttonpos;
int groupcnt;
int listflag;
int textpos;
struct G_OBJECT **objlist;
struct G_GROUP **grplist;
struct G_VALUE **valent;
struct G_GROUP *group;
grplist = &global.groups;
objlist = &global.objects;
valent = NULL;
textpos = menupos = buttonpos = 0;
groupcnt = 0;
/*
listflag = 0;
*/
listflag = 1;
if (init_config(cfname)) return(2);
while((toktype = get_token(buf1, buf2, buf3)) > TOK_EOF)
{
switch(toktype)
{
case TOK_TEXT: global.text[textpos] = savestr(buf1);
if (textpos++ > MAX_TEXT) goto error;
break;
case TOK_BAR: global.menuitem[menupos].nm_Type = MENU_ITEM;
global.menuitem[menupos].nm_Label = NM_BARLABEL;
if (menupos++ > MAX_MENU) goto error;
break;
case TOK_MENU: global.menuitem[menupos].nm_Type = MENU_MENU;
global.menuitem[menupos].nm_Label = savestr(buf1);
if (menupos++ > MAX_MENU) goto error;
break;
case TOK_ITEM: global.menuitem[menupos].nm_Type = MENU_ITEM;
global.menuitem[menupos].nm_Label = savestr(buf1);
if (buf2[0])
global.menuitem[menupos].nm_CommKey = savestr(buf2);
global.menuitem[menupos].nm_UserData = savestr(buf3);
if (menupos++ > MAX_MENU) goto error;
break;
case TOK_BUTTON: if (buttonpos > MAX_BUTTON) goto error;
global.button[buttonpos++].title = savestr(buf1);
break;
case TOK_TITLE: strcpy(global.wtitle, buf1);
break;
case TOK_LGROUP:
case TOK_GROUP: grplist = (struct G_GROUP **)
newobj(
(struct G_OBJECT **)grplist,
CLASS_GROUP, buf1);
if (!grplist) return(1);
objlist = &(((struct G_GROUP *)grplist)->objects);
valent = NULL;
/*
(*grplist)->local = toktype == TOK_LGROUP ? 1 : 0;
*/
((struct G_GROUP *)grplist)->local
= (toktype == TOK_LGROUP ? 1 : 0);
if (groupcnt > global.maxsize) global.maxsize = groupcnt;
groupcnt = 2;
break;
case TOK_STRING: objlist = newobj(objlist, CLASS_STRING, buf1);
if (!objlist) return(1);
groupcnt++;
((struct G_STRING *)objlist)->option = savestr(buf2);
valent = NULL;
break;
case TOK_CHECK: objlist = newobj(objlist, CLASS_CHECK, buf1);
if (!objlist) return(1);
groupcnt++;
((struct G_CHECK *)objlist)->option0 = savestr(buf2);
((struct G_CHECK *)objlist)->option1 = savestr(buf3);
valent = NULL;
break;
case TOK_LIST: objlist = newobj(objlist, CLASS_LIST, buf1);
if (!objlist) return(1);
groupcnt += 3;
/* lists are just a bit bigger than three lines, */
/* so fudge on the first one, a few more will fit */
/*
listflag = !listflag;
*/
groupcnt += listflag;
/* Next line replaces trick above to add half ine per list */
listflag = 0;
((struct G_LIST *)objlist)->option = savestr(buf2);
valent = NULL;
break;
case TOK_CYCLE: objlist = newobj(objlist, CLASS_CYCLE, buf1);
if (!objlist) return(1);
groupcnt++;
valent = &((struct G_CYCLE *)objlist)->values;
break;
case TOK_VALUE: if (valent == NULL) goto error;
*valent = get_mem(sizeof(struct G_VALUE));
if (!*valent) return(1);
(*valent)->next = NULL;
(*valent)->title = savestr(buf1);
(*valent)->option = savestr(buf2);
(*valent)->string = NULL;
if (strchr(buf2, '%'))
{
struct G_STRING *gstr;
gstr = get_mem(sizeof(struct G_STRING));
if (!gstr) return(1);
gstr->base.class = CLASS_STRING;
(*valent)->string = gstr;
}
valent = &((*valent)->next);
break;
}
}
if (groupcnt > global.maxsize) global.maxsize = groupcnt;
global.curgroup = global.groups;
global.menuitem[menupos].nm_Type = MENU_END;
if (toktype == TOK_ERROR)
{
error:
sprintf(global.title, "ERROR- %s line %d", cfname, global.line);
return(1);
}
close_config();
/* Now we need to go through the groups and sanitize the pointers as well */
/* as initialize all the default states */
init_group(global.objects);
for(group = global.groups; group != NULL;
group = (struct G_GROUP *)group->base.next)
{
if (group->base.next) group->base.next->prev = (struct G_OBJECT *)group;
init_group(group->objects);
}
return(0);
}
/*
* Open the configuration file and initialize any global data
*/
int init_config(char *cfname)
{
struct Process *mytask;
APTR saveptr;
global.state = ST_SCN;
global.line = 1;
mytask = (struct Process *)FindTask(NULL);
saveptr = mytask->pr_WindowPtr;
mytask->pr_WindowPtr = (APTR)-1;
global.fp = fopen(cfname, "r");
mytask->pr_WindowPtr = saveptr;
if (global.fp == NULL)
{
sprintf(global.title, "ERROR- No %s", cfname);
return(1);
}
return(0);
}
/*
* Close the configuration file and clean up anything else necessary
*/
void close_config()
{
if (global.fp)
fclose(global.fp);
global.fp = NULL;
}